package com.augurit.smartform.database.sqlserver;

import java.sql.CallableStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import com.augurit.smartform.constant.SqlTypeConstant;
import com.augurit.smartform.database.LockMode;
import com.augurit.smartform.database.dialect.Dialect;

public class SQLServerDialect extends Dialect {
	public SQLServerDialect(){
		super();
		registerColumnType( SqlTypeConstant.BIT, "tinyint" );
		registerColumnType( SqlTypeConstant.BIGINT, "numeric(19,0)" );
		registerColumnType( SqlTypeConstant.SMALLINT, "smallint" );
		registerColumnType( SqlTypeConstant.TINYINT, "tinyint" );
		registerColumnType( SqlTypeConstant.INTEGER, "int" );
		registerColumnType( SqlTypeConstant.CHAR, "char(1)" );
		registerColumnType( SqlTypeConstant.VARCHAR, "varchar($l)" );
		registerColumnType( SqlTypeConstant.FLOAT, "float" );
		registerColumnType( SqlTypeConstant.DOUBLE, "double precision" );
		registerColumnType( SqlTypeConstant.DATE, "datetime" );
		registerColumnType( SqlTypeConstant.TIME, "datetime" );
		registerColumnType( SqlTypeConstant.TIMESTAMP, "datetime" );
		registerColumnType( SqlTypeConstant.VARBINARY, "varbinary($l)" );
		registerColumnType( SqlTypeConstant.NUMERIC, "numeric($p,$s)" );
		registerColumnType( SqlTypeConstant.BLOB, "image" );
		registerColumnType( SqlTypeConstant.CLOB, "text" );
		registerKeyword("top");
	}
	
	public String getNoColumnsInsertString() {
		return "default values";
	}
	
	static int getAfterSelectInsertPoint(String sql) {
		int selectIndex = sql.toLowerCase().indexOf( "select" );
		final int selectDistinctIndex = sql.toLowerCase().indexOf( "select distinct" );
		return selectIndex + ( selectDistinctIndex == selectIndex ? 15 : 6 );
	}
	
	public String getLimitString(String querySelect, int offset, int limit) {
		if ( offset > 0 ) {
			throw new UnsupportedOperationException( "sql server has no offset" );
		}
		return new StringBuffer( querySelect.length()+8 )
			.append(querySelect)
			.insert( getAfterSelectInsertPoint(querySelect), " top " + limit )
			.toString();
	}
	
	public String appendIdentitySelectToInsert(String insertSQL) {
		return insertSQL + " select scope_identity()";
	}
	
	public boolean supportsLimit() {
		return true;
	}

	public boolean useMaxForLimit() {
		return true;
	}
	
	public boolean supportsLimitOffset() {
		return false;
	}

	public boolean supportsVariableLimit() {
		return false;
	}

	public char closeQuote() {
		return ']';
	}

	public char openQuote() {
		return '[';
	}
	
	public String appendLockHint(LockMode mode, String tableName) {
		if ( mode.greaterThan(LockMode.READ) ) {
			// does this need holdlock also? : return tableName + " with (updlock, rowlock, holdlock)";
			return tableName + " with (updlock, rowlock)";
		}
		else {
			return tableName;
		}
	}
	
	public String getSelectGUIDString() {
		return "select newid()";
	}

	// The current_timestamp is more accurate, but only known to be supported
	// in SQL Server 7.0 and later (i.e., Sybase not known to support it at all)
	public String getCurrentTimestampSelectString() {
		return "select current_timestamp";
	}
	
	public String getAddColumnString() {
		return "add";
	}
	public String getNullColumnString() {
		return " null";
	}
	public boolean qualifyIndexName() {
		return false;
	}

	public String getForUpdateString() {
		return "";
	}
	
	public boolean supportsIdentityColumns() {
		return true;
	}
	
	public String getIdentitySelectString() {
		return "select @@identity";
	}
	
	public String getIdentityColumnString() {
		return "identity not null"; //starts with 1, implicitly
	}
	
	public boolean supportsInsertSelectIdentity() {
		return true;
	}
	
	public int registerResultSetOutParameter(CallableStatement statement, int col) throws SQLException {
		return col; // sql server just returns automatically
	}
	
	public ResultSet getResultSet(CallableStatement ps) throws SQLException {
		boolean isResultSet = ps.execute(); 
//		 This assumes you will want to ignore any update counts 
		while (!isResultSet && ps.getUpdateCount() != -1) { 
		    isResultSet = ps.getMoreResults(); 
		} 
		ResultSet rs = ps.getResultSet(); 
//		 You may still have other ResultSets or update counts left to process here 
//		 but you can't do it now or the ResultSet you just got will be closed 
		return rs;
	}
	
	public boolean supportsCurrentTimestampSelection() {
		return true;
	}

	public boolean isCurrentTimestampSelectStringCallable() {
		return false;
	}
	
	public boolean supportsTemporaryTables() {
		return true;
	}

	public String generateTemporaryTableName(String baseTableName) {
		return "#" + baseTableName;
	}

	public boolean dropTemporaryTableAfterUse() {
		return true;  // sql-server, at least needed this dropped after use; strange!
	}
}
